package org.jeecg.modules.login.handler.ding;

import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders;
import com.aliyun.dingtalkcontact_1_0.models.GetUserResponseBody;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;

/**
 * 钉钉执行器
 *
 * @author tianyi.jiang
 * @since 1.0.0
 */
@Slf4j
@Component
@ConditionalOnExpression("'${basicplatform.ding.enable:false}'.equals('true')")
public class DingHandler {

    private final DingProperty dingProperty;

    /**
     * 授权码获取用户(扫码登录)
     *
     * @param authCode 授权码
     * @return 用户信息
     */
    @SneakyThrows
    public GetUserResponseBody dingUser(@Nonnull String authCode) {
        Config config = new Config() {{
            this.protocol = "https";
            this.regionId = "central";
        }};
        com.aliyun.dingtalkoauth2_1_0.Client oauthClient = new com.aliyun.dingtalkoauth2_1_0.Client(config);
        GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
                .setClientId(dingProperty.getAppKey())
                .setClientSecret(dingProperty.getAppSecret())
                .setCode(authCode)
                .setGrantType("authorization_code");
        GetUserTokenResponse userTokenResponse = oauthClient.getUserToken(getUserTokenRequest);
        // 用户 access token
        String accessToken = userTokenResponse.getBody().getAccessToken();

        com.aliyun.dingtalkcontact_1_0.Client contactClient = new com.aliyun.dingtalkcontact_1_0.Client(config);
        GetUserHeaders getUserHeaders = new GetUserHeaders();
        getUserHeaders.xAcsDingtalkAccessToken = accessToken;
        // 用户个人信息, 如需获取当前授权人的信息, unionId参数必须传me
        return contactClient.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody();
    }

    /**
     * 获取钉钉用户ID
     *
     * @param unionId 用户在应用所属企业唯一标识
     * @return 钉钉用户ID
     */
    @SneakyThrows
    public String dingIdByUnionId(@Nonnull String unionId) {
        DingTalkClient dingTalkClient = new DefaultDingTalkClient(dingProperty.getDingIdUrl());
        OapiUserGetbyunionidRequest oapiUserGetbyunionidRequest = new OapiUserGetbyunionidRequest();
        oapiUserGetbyunionidRequest.setUnionid(unionId);
        OapiUserGetbyunionidResponse oapiUserGetbyunionidResponse = dingTalkClient.execute(oapiUserGetbyunionidRequest, dingProperty.accessToken());
        if (!oapiUserGetbyunionidResponse.isSuccess()) {
            throw new JeecgBootException("[DING DING] Fail to get user id by union id.");
        }
        return oapiUserGetbyunionidResponse.getResult().getUserid();
    }

    /**
     * 获取钉钉用户ID
     *
     * @param mobile 电话号码
     * @return 钉钉用户ID
     */
    @SneakyThrows
    public String dingIdByPhone(@Nonnull String mobile) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getbymobile");
        OapiV2UserGetbymobileRequest oapiV2UserGetbymobileRequest = new OapiV2UserGetbymobileRequest();
        oapiV2UserGetbymobileRequest.setMobile(mobile);
        OapiV2UserGetbymobileResponse oapiV2UserGetbymobileResponse = client.execute(oapiV2UserGetbymobileRequest, dingProperty.accessToken());
        if (!oapiV2UserGetbymobileResponse.isSuccess()) {
            throw new JeecgBootException("[DING DING] Fail to get user id by mobile.");
        }
        return oapiV2UserGetbymobileResponse.getResult().getUserid();
    }

    /**
     * 发送消息
     *
     * @param dingId 钉钉用户ID
     * @param text   文本
     */
    @SneakyThrows
    public void send(@Nonnull String dingId, @Nonnull String text) {
        DingTalkClient dingTalkClient = new DefaultDingTalkClient(dingProperty.getMessageUrl());
        OapiMessageCorpconversationAsyncsendV2Request oapiMessageCorpconversationAsyncsendV2Request = new OapiMessageCorpconversationAsyncsendV2Request();
        oapiMessageCorpconversationAsyncsendV2Request.setAgentId(dingProperty.getAgentId());
        oapiMessageCorpconversationAsyncsendV2Request.setUseridList(dingId);
        oapiMessageCorpconversationAsyncsendV2Request.setToAllUser(Boolean.FALSE);
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype("text");
        msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());
        msg.getText().setContent(text);
        oapiMessageCorpconversationAsyncsendV2Request.setMsg(msg);
        OapiMessageCorpconversationAsyncsendV2Response oapiMessageCorpconversationAsyncsendV2Response =
                dingTalkClient.execute(oapiMessageCorpconversationAsyncsendV2Request, dingProperty.accessToken());
        log.info("[DING DING] Send message api response: {}", oapiMessageCorpconversationAsyncsendV2Response.toString());
        if (!oapiMessageCorpconversationAsyncsendV2Response.isSuccess()) {
            throw new JeecgBootException("[DING DING] Fail to send ding ding message.");
        }
    }

    /**
     * 群聊机器人发送
     *
     * @param text 文本
     */
    @SneakyThrows
    public void sendByRobot(@Nonnull String text) {
        OapiRobotSendRequest request = new OapiRobotSendRequest() {{
            this.setMsgtype("text");
            Text textEntry = new Text();
            textEntry.setContent(text);
            this.setText(textEntry);
        }};
        // 群聊机器人地址
        String robotUrl = dingProperty.getRobotUrl();
        // 群聊机器人加签
        String robotSecret = dingProperty.getRobotSecret();
        if (StringUtils.isNotEmpty(robotSecret)) {
            long timestamp = System.currentTimeMillis();
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(robotSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signBytes = mac.doFinal((timestamp + "\n" + robotSecret).getBytes(StandardCharsets.UTF_8));
            String sign = URLEncoder.encode(new String(Base64.encodeBase64(signBytes)), StandardCharsets.UTF_8.name());
            robotUrl = robotUrl + String.format("&sign=%s&timestamp=%d", sign, timestamp);
        }
        OapiRobotSendResponse oapiRobotSendResponse = new DefaultDingTalkClient(robotUrl).execute(request);
        if (!oapiRobotSendResponse.isSuccess()) {
            throw new JeecgBootException("[DING DING] Fail to send ding ding robot message.");
        }
    }

    public DingHandler(DingProperty dingProperty) {
        this.dingProperty = dingProperty;
    }

    @Slf4j
    @Data
    @Configuration
    @ConfigurationProperties(prefix = "basicplatform.ding")
    public static class DingProperty {
        /**
         * 请求access token地址
         */
        private String accessTokenUrl = "https://oapi.dingtalk.com/gettoken";
        /**
         * 授权后重定向认证地址
         */
        private String authUrl;
        /**
         * 发送消息地址
         */
        private String messageUrl = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2";
        /**
         * 获取用户Id地址
         */
        private String dingIdUrl = "https://oapi.dingtalk.com/topapi/user/getbyunionid";
        /**
         * [群聊机器人] 地址
         */
        private String robotUrl;
        /**
         * [群聊机器人] 加签
         */
        private String robotSecret;
        /**
         * 认证Key
         */
        private String appKey;
        /**
         * 认证秘钥
         */
        private String appSecret;
        /**
         * 应用Id
         */
        private Long agentId;

        /**
         * 获取令牌
         *
         * @return access token
         */
        @SneakyThrows
        protected String accessToken() {
            OapiGettokenRequest oapiGettokenRequest = new OapiGettokenRequest();
            oapiGettokenRequest.setAppkey(this.getAppKey());
            oapiGettokenRequest.setAppsecret(this.getAppSecret());
            oapiGettokenRequest.setHttpMethod(HttpMethod.GET.name());
            DingTalkClient dingTalkClient = new DefaultDingTalkClient(this.getAccessTokenUrl());
            OapiGettokenResponse oapiGettokenResponse = dingTalkClient.execute(oapiGettokenRequest);
            log.info("[DING DING] Access token api response: {}", oapiGettokenResponse.toString());
            return oapiGettokenResponse.getAccessToken();
        }

        /**
         * 转换登录地址
         *
         * @return 登录地址
         */
        @SneakyThrows
        public String loginUrl() {
            String template = "https://login.dingtalk.com/oauth2/auth?redirect_uri={0}&response_type=code&client_id={1}&scope=openid&prompt=consent";
            String encodeUrl = URLEncoder.encode(this.getAuthUrl(), StandardCharsets.UTF_8.name());
            return MessageFormat.format(template, encodeUrl, this.getAppKey());
        }
    }

}
